Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit a40a27c88087dbae6273f62fd237b9113378cedd


Parents : 9f75e3d
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-21T16:56:42-05:00

refactor: optimize Dockerfile for improved build efficiency and layer management

Changes

3 files changed, 74 insertions(+), 35 deletions(-)

M Dockerfile +38 -18

Diff

diff --git a/Dockerfile b/Dockerfile
index b0622c69..cd909dc0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# Docker Build Stages:
# 1. build-frontend: Build static frontend assets using Node
-# 2. builder: Install Python dependencies, build and collect backend files in a venv
-# 3. final image: Copy venv, install runtime deps, set up container user and config
+# 2. builder: Install third-party deps into /opt/venv-deps, then app overlay
+# 3. final image: runtime pkgs, deps layer, thin app overlay (better update pulls)
#
# LXST wheels ship glibc-tagged filterlib extensions only. On Alpine/musl, cffi
# compiles at build time; scripts/docker-bake-lxst-filterlib-musl.py copies the
@@ -56,6 +56,7 @@ RUN pip install --no-cache-dir --upgrade "pip>=26.0" uv setuptools wheel "jaraco
# Create the clean venv for our application dependencies
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
+ENV UV_PROJECT_ENVIRONMENT=/opt/venv
# Install essential runtime tools in the venv (cffi verify needs setuptools on Python 3.12+)
RUN pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.context>=6.1.0"
@@ -63,32 +64,45 @@ RUN pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.contex
COPY pyproject.toml uv.lock README.md CHANGELOG.md ./
COPY logo ./logo
COPY vendor ./vendor
-RUN uv sync --no-group dev --no-install-project && \
- rm -rf /root/.cache/pip /root/.cache/uv
-
-COPY meshchatx ./meshchatx
COPY scripts/docker-bake-lxst-filterlib-musl.py ./scripts/docker-bake-lxst-filterlib-musl.py
COPY scripts/patch_lxst_pyogg_ogg_ctypes.py ./scripts/patch_lxst_pyogg_ogg_ctypes.py
COPY scripts/patch_lxst_codec2_optional.py ./scripts/patch_lxst_codec2_optional.py
-COPY --from=build-frontend /src/meshchatx/public ./meshchatx/public
-
-RUN pip install --no-cache-dir . && \
+# Third-party deps layer: stable across app-only updates when uv.lock is unchanged.
+# --inexact keeps setuptools/jaraco.context already in the venv (cffi bake needs them).
+RUN uv sync --no-group dev --no-install-project --inexact && \
+ rm -rf /root/.cache/pip /root/.cache/uv && \
+ pip install --no-cache-dir --upgrade "setuptools" "jaraco.context>=6.1.0" && \
python scripts/patch_lxst_pyogg_ogg_ctypes.py && \
python scripts/patch_lxst_codec2_optional.py && \
python scripts/docker-bake-lxst-filterlib-musl.py && \
rm -rf /opt/venv/lib/python*/site-packages/LXST/Platforms/android && \
- find /opt/venv -type d -name "tests" -exec rm -rf {} + && \
- find /opt/venv -type d -name "test" -exec rm -rf {} + && \
- find /opt/venv -type d -name "__pycache__" -exec rm -rf {} + && \
- python -m compileall /opt/venv/lib/python3.14/site-packages
+ find /opt/venv -type d \( -name tests -o -name test -o -name __pycache__ \) -prune -exec rm -rf {} + && \
+ python -m compileall -q /opt/venv/lib/python3.14/site-packages && \
+ cp -a /opt/venv /opt/venv-deps
+
+COPY meshchatx ./meshchatx
+COPY --from=build-frontend /src/meshchatx/public ./meshchatx/public
+
+# App overlay: meshchatx + vendored packages + console scripts (changes often).
+RUN pip install --no-cache-dir . && \
+ PYVER="$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')" && \
+ SP="/opt/venv/lib/python${PYVER}/site-packages" && \
+ OUT="/opt/app-overlay" && \
+ mkdir -p "${OUT}/lib/python${PYVER}/site-packages" "${OUT}/bin" && \
+ for pkg in meshchatx lxmfy rns_filesync; do \
+ cp -a "${SP}/${pkg}" "${OUT}/lib/python${PYVER}/site-packages/"; \
+ done && \
+ cp -a "${SP}"/reticulum_meshchatx*.dist-info "${OUT}/lib/python${PYVER}/site-packages/" && \
+ for cmd in meshchat meshchatx meshchatx-repository-http lxmfy rns-filesync; do \
+ cp -a "/opt/venv/bin/${cmd}" "${OUT}/bin/"; \
+ done && \
+ find "${OUT}" -type d -name __pycache__ -prune -exec rm -rf {} + && \
+ python -m compileall -q "${OUT}/lib/python${PYVER}/site-packages"
# ---- STAGE 3: Final Image ----
+# Layer order matters for update pulls: base runtime, third-party venv, thin app overlay.
FROM ${PYTHON_IMAGE}@${PYTHON_HASH}
-ARG OCI_REVISION=""
-ARG OCI_VERSION=""
-ARG OCI_CREATED=""
-
RUN apk upgrade --no-cache && \
apk add --no-cache opusfile libffi espeak-ng su-exec && \
python -m pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.context>=6.1.0" && \
@@ -96,10 +110,16 @@ RUN apk upgrade --no-cache && \
addgroup -g 1000 meshchat && adduser -u 1000 -G meshchat -S meshchat && \
mkdir -p /config && chown meshchat:meshchat /config
-COPY --from=builder --chown=meshchat:meshchat /opt/venv /opt/venv
+COPY --from=builder --chown=meshchat:meshchat /opt/venv-deps /opt/venv
+COPY --from=builder --chown=meshchat:meshchat /opt/app-overlay/ /opt/venv/
COPY scripts/docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
+# Declare after COPY so per-build OCI values do not invalidate runtime or venv layers.
+ARG OCI_REVISION=""
+ARG OCI_VERSION=""
+ARG OCI_CREATED=""
+
LABEL org.opencontainers.image.source="https://github.com/Quad4-Software/MeshChatX"
LABEL org.opencontainers.image.description="MeshChatX is a all in one Reticulum client."
LABEL org.opencontainers.image.licenses="MIT AND 0BSD"

diff --git a/Dockerfile.hardened b/Dockerfile.hardened
index 0b8855ce..bc34cff5 100644
--- a/Dockerfile.hardened
+++ b/Dockerfile.hardened
@@ -49,34 +49,47 @@ RUN pip install --no-cache-dir --upgrade "pip>=26.0" uv setuptools wheel "jaraco
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
+ENV UV_PROJECT_ENVIRONMENT=/opt/venv
RUN pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.context>=6.1.0"
COPY pyproject.toml uv.lock README.md CHANGELOG.md ./
COPY logo ./logo
COPY vendor ./vendor
-RUN uv sync --no-group dev --no-install-project && \
- rm -rf /root/.cache/pip /root/.cache/uv
-
-COPY meshchatx ./meshchatx
COPY scripts/patch_lxst_pyogg_ogg_ctypes.py ./scripts/patch_lxst_pyogg_ogg_ctypes.py
COPY scripts/patch_lxst_codec2_optional.py ./scripts/patch_lxst_codec2_optional.py
-COPY --from=build-frontend /src/meshchatx/public ./meshchatx/public
-
-RUN pip install --no-cache-dir . && \
+# Third-party deps layer: stable across app-only updates when uv.lock is unchanged.
+# --inexact keeps setuptools/jaraco.context already in the venv (cffi needs them).
+RUN uv sync --no-group dev --no-install-project --inexact && \
+ rm -rf /root/.cache/pip /root/.cache/uv && \
+ pip install --no-cache-dir --upgrade "setuptools" "jaraco.context>=6.1.0" && \
python scripts/patch_lxst_pyogg_ogg_ctypes.py && \
python scripts/patch_lxst_codec2_optional.py && \
rm -rf /opt/venv/lib/python*/site-packages/LXST/Platforms/android && \
- find /opt/venv -type d -name "tests" -exec rm -rf {} + && \
- find /opt/venv -type d -name "test" -exec rm -rf {} + && \
- find /opt/venv -type d -name "__pycache__" -exec rm -rf {} + && \
- python -m compileall -q /opt/venv
+ find /opt/venv -type d \( -name tests -o -name test -o -name __pycache__ \) -prune -exec rm -rf {} + && \
+ python -m compileall -q /opt/venv && \
+ cp -a /opt/venv /opt/venv-deps
-FROM ${PYTHON_RUNTIME_IMAGE}
+COPY meshchatx ./meshchatx
+COPY --from=build-frontend /src/meshchatx/public ./meshchatx/public
-ARG OCI_REVISION=""
-ARG OCI_VERSION=""
-ARG OCI_CREATED=""
+# App overlay: meshchatx + vendored packages + console scripts (changes often).
+RUN pip install --no-cache-dir . && \
+ PYVER="$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')" && \
+ SP="/opt/venv/lib/python${PYVER}/site-packages" && \
+ OUT="/opt/app-overlay" && \
+ mkdir -p "${OUT}/lib/python${PYVER}/site-packages" "${OUT}/bin" && \
+ for pkg in meshchatx lxmfy rns_filesync; do \
+ cp -a "${SP}/${pkg}" "${OUT}/lib/python${PYVER}/site-packages/"; \
+ done && \
+ cp -a "${SP}"/reticulum_meshchatx*.dist-info "${OUT}/lib/python${PYVER}/site-packages/" && \
+ for cmd in meshchat meshchatx meshchatx-repository-http lxmfy rns-filesync; do \
+ cp -a "/opt/venv/bin/${cmd}" "${OUT}/bin/"; \
+ done && \
+ find "${OUT}" -type d -name __pycache__ -prune -exec rm -rf {} + && \
+ python -m compileall -q "${OUT}/lib/python${PYVER}/site-packages"
+
+FROM ${PYTHON_RUNTIME_IMAGE}
USER root
RUN apk add --no-cache opus libffi shadow && \
@@ -87,9 +100,15 @@ RUN apk add --no-cache opus libffi shadow && \
--shell /sbin/nologin meshchat && \
mkdir -p /config && chown meshchat:meshchat /config
-COPY --from=builder --chown=meshchat:meshchat /opt/venv /opt/venv
+COPY --from=builder --chown=meshchat:meshchat /opt/venv-deps /opt/venv
+COPY --from=builder --chown=meshchat:meshchat /opt/app-overlay/ /opt/venv/
COPY scripts/docker_entrypoint_chainguard.py /docker-entrypoint.py
+# Declare after COPY so per-build OCI values do not invalidate runtime or venv layers.
+ARG OCI_REVISION=""
+ARG OCI_VERSION=""
+ARG OCI_CREATED=""
+
LABEL org.opencontainers.image.source="https://github.com/Quad4-Software/MeshChatX"
LABEL org.opencontainers.image.description="MeshChatX is a all in one Reticulum client."
LABEL org.opencontainers.image.licenses="MIT AND 0BSD"
@@ -109,4 +128,4 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=90s --retries=3 \
CMD ["python", "-c", "import ssl, urllib.request; urllib.request.urlopen('https://127.0.0.1:8000/api/v1/status', context=ssl._create_unverified_context())"]
ENTRYPOINT ["/usr/bin/python", "/docker-entrypoint.py"]
-CMD ["/opt/venv/bin/meshchatx", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.reticulum-meshchatx", "--headless"]
+CMD ["/opt/venv/bin/meshchatx", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.reticulum-meshchatx", "--headless"]
\ No newline at end of file

diff --git a/meshchatx.rsm b/meshchatx.rsm
index 444f3dfe..a84ea4e3 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────